#include "Task.hpp"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

const int processnum = 10;
std::vector<task_t> tasks;


//先描述
class channel
{
public:
    channel(int cmdfd, int slaverid, const std::string & processname)
    :_cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {}
public:
    int _cmdfd;
    pid_t _slaverid;
    std::string _processname;
};

void slaver()
{
    //read(0);
    while(true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int));//如果父进程不给子进程发送数据---阻塞等待！！！
        if (n == sizeof(int))
        {
            //执行cmdcode对应的任务列表
            std::cout << "pid:" << getpid() << " - slaver's cmdcode is" << cmdcode << std::endl;
            //std::cout << "slaver says@i get a command " << getpid() << " : cmdcode: " << cmdcode << std::endl;
            if (cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }

        if (n == 0) break;
    }
}

// 输入：const &
// 输出：*
// 输入输出：&
void InitProcessPool(std::vector<channel> *channels)
{
    //version2 - 确保每一个子进程都只有一个写端
    std::vector<int> oldfds;
    for (int i = 0; i < processnum; i++)
    {
        int pipefd[2];//临时空间
        int n = pipe(pipefd);
        assert(!n);
        (void)n;

        pid_t id = fork();
        if (id == 0)//child
        {
            std::cout << "child: " << getpid() << " close history fd: ";
            for (auto fd : oldfds)
            {
                std::cout << fd << " ";
                close(fd);
            }
            std::cout << "\n";

            close(pipefd[1]);
            dup2(pipefd[0], 0);
            close(pipefd[0]);
            slaver();
            std::cout << "process : " << getpid() << "quit" << std::endl;

            exit(0);
        }


        //father
        close(pipefd[0]);

        //添加channel字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name)); 
        oldfds.push_back(pipefd[1]);

        sleep(1);
    }
}

void Menu()
{
    std::cout << "################################################" << std::endl;
    std::cout << "### 1. 刷新日志            2. 刷新出来野怪    ####" << std::endl;
    std::cout << "### 3. 检测软件是否更新     4. 更新用的血量和蓝量###" << std::endl;
    std::cout << "###                        0. 退出            ###" << std::endl;
    std::cout << "#################################################" << std::endl;
}

void ctrlSlaver(const std::vector<channel> &channels)
{
    int which_channel = 0;

    while (true)
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@ ";
        std::cin >> select;

        if (select <= 0 && select >= 5) break;
        

        int cmdcode = select - 1;

        std::cout << "father got cmdcode:" << cmdcode 
        << " and sent to pid-" << channels[which_channel]._slaverid << "-slaver"
        <<" whose name's " << channels[which_channel]._processname << std::endl;

        // std::cout << "father says@" << " cmdcode:" << cmdcode        
        // << " already send to " << channels[which_channel]._slaverid 
        // << " process name:" << channels[which_channel]._processname
        // << std::endl;

        //send message
        write(channels[which_channel]._cmdfd, &cmdcode, sizeof(cmdcode));


        which_channel++;
        which_channel %= channels.size();
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    //version2
    for (const auto &c : channels)
    {
        close(c._cmdfd);
        waitpid(c._slaverid, nullptr, 0);
    }

    // version1 - 倒着回收
    // int last = channels.size()-1;
    // for(int i = last; i >= 0; i--)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverid, nullptr, 0);
    // }
    
    //最后一个管道的写端被关闭 - 则read的结果为0 - 子进程倒着退出
    // for(const auto &c : channels) close(c._cmdfd);
    // // sleep(5);
    // for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);
    // // sleep(5);
}

int main()
{
    LoadTask(&tasks);

    srand(time(nullptr)^getpid()^1023); // 种一个随机数种子

    //再组织
    std::vector<channel> channels;

    //1.初始化 
    InitProcessPool(&channels);
    
    // //test
    // for (auto &c : channels)
    // {
    //     std::cout << c._cmdfd << " " << c._slaverid << " " << c._processname << std::endl;
    // }

    //2.开始控制子进程
    ctrlSlaver(channels);

    //3.清理收尾
    QuitProcess(channels);

    return 0;
}